package io.shuttle.mbe.api

import io.shuttle.mbe.api.annotation.ChromeMinVersion
import io.shuttle.mbe.api.annotation.ChromeOSOnly
import io.shuttle.mbe.api.annotation.PromiseStyleMinVersion
import io.shuttle.mbe.api.types.ArrayBuffer
import io.shuttle.mbe.api.types.EventTarget
import io.shuttle.mbe.api.types.Value1Function
import io.shuttle.mbe.api.types.Value2Function
import io.shuttle.mbe.api.types.Value3Function
import io.shuttle.mbe.api.types.VoidFunction
import io.shuttle.mbe.core.Promise

////////////////////
// VPN Provider
////////////////////
/**
 * Use the `Browser.vpnProvider` API to implement a VPN client.
 *
 * Permissions: "vpnProvider"
 * @platform ChromeOS only
 * @since Chrome 43
 */
@ChromeOSOnly
@ChromeMinVersion(43)
interface VpnProvider {
    // Creates a new VPN configuration that persists across multiple login sessions of the user.
    // @callback id - A unique ID for the created configuration, or undefined on failure.
    @PromiseStyleMinVersion(96)
    fun createConfig(
        // The name of the VPN configuration.
        name: String,
        callback: Value1Function<String>? = null
    ): Promise<String>

    // Destroys a VPN configuration created by the extension.
    @PromiseStyleMinVersion(96)
    fun destroyConfig(
        // ID of the VPN configuration to destroy.
        id: String,
        callback: VoidFunction? = null
    ): Promise<Void>

    // Notifies the VPN session state to the platform. This will succeed only when the VPN session is owned by the extension.
    @PromiseStyleMinVersion(96)
    fun notifyConnectionStateChanged(
        // The VPN session state of the VPN client.
        state: VpnConnectionState,
        callback: VoidFunction? = null
    ): Promise<Void>

    // Sends an IP packet through the tunnel created for the VPN session. This will succeed only when the VPN session is owned by the extension.
    @PromiseStyleMinVersion(96)
    fun sendPacket(
        // The IP packet to be sent to the platform.
        data: ArrayBuffer,
        callback: VoidFunction? = null
    ): Promise<Void>

    // Sets the parameters for the VPN session. This should be called immediately after "connected" is received from the platform. This will succeed only when the VPN session is owned by the extension.
    @PromiseStyleMinVersion(96)
    fun setParameters(
        parameters: Parameters,
        callback: VoidFunction? = null
    ): Promise<Void>

    // Triggered when a configuration is created by the platform for the extension.
    // (id: string, name: string, data: object) => void
    val onConfigCreated: EventTarget<Value3Function<String, String, Map<String, Any>>>

    // Triggered when a configuration created by the extension is removed by the platform.
    val onConfigRemoved: EventTarget<Value1Function<String>>

    // Triggered when an IP packet is received via the tunnel for the VPN session owned by the extension
    val onPacketReceived: EventTarget<Value1Function<ArrayBuffer>>

    // Triggered when a message is received from the platform for a VPN configuration owned by the extension.
    // (id: string, message: PlatformMessage, error: string) => void
    val onPlatformMessage: EventTarget<Value3Function<String, PlatformMessage, String>>

    // Triggered when there is a UI event for the extension. UI events are signals from the platform that indicate to the app that a UI dialog needs to be shown to the user.
    // (event: UIEvent, id?: string) => void
    val onUIEvent: EventTarget<Value2Function<UIEvent, String?>>

    data class Parameters(
        // IP address for the VPN interface in CIDR notation. IPv4 is currently the only supported mode.
        var address: String,
        // Broadcast address for the VPN interface. (default: deduced from IP address and mask)
        var broadcastAddress: String?,
        // A list of IPs for the DNS servers.
        var dnsServers: List<String>?,
        // A list of search domains. (default: no search domain)
        var domainSearch: List<String>?,
        // Exclude network traffic to the list of IP blocks in CIDR notation from the tunnel. This can be used to bypass traffic to and from the VPN server. When many rules match a destination, the rule with the longest matching prefix wins. Entries that correspond to the same CIDR block are treated as duplicates. Such duplicates in the collated (exclusionList + inclusionList) list are eliminated and the exact duplicate entry that will be eliminated is undefined.
        var exclusionList: List<String>,
        // Include network traffic to the list of IP blocks in CIDR notation to the tunnel. This parameter can be used to set up a split tunnel. By default no traffic is directed to the tunnel. Adding the entry "0.0.0.0/0" to this list gets all the user traffic redirected to the tunnel. When many rules match a destination, the rule with the longest matching prefix wins. Entries that correspond to the same CIDR block are treated as duplicates. Such duplicates in the collated (exclusionList + inclusionList) list are eliminated and the exact duplicate entry that will be eliminated is undefined.
        var inclusionList: List<String>,
        // MTU setting for the VPN interface. (default: 1500 bytes)
        var mtu: String?,
        // Whether or not the VPN extension implements auto-reconnection.
        //
        //If true, the linkDown, linkUp, linkChanged, suspend, and resume platform messages will be used to signal the respective events. If false, the system will forcibly disconnect the VPN if the network topology changes, and the user will need to reconnect manually. (default: false)
        //
        //This property is new in Chrome 51; it will generate an exception in earlier versions. try/catch can be used to conditionally enable the feature based on browser support.
        @ChromeMinVersion(51)
        var reconnect: String?,
    )

    // The enum is used by the platform to notify the client of the VPN session status.
    enum class PlatformMessage {
        // Indicates that the VPN configuration connected.
        connected,

        // Indicates that the VPN configuration disconnected.
        disconnected,

        // Indicates that an error occurred in VPN connection, for example a timeout. A description of the error is given as the error argument to onPlatformMessage.
        error,

        // Indicates that the default physical network connection is down.
        linkDown,

        // Indicates that the default physical network connection is back up.
        linkUp,

        // Indicates that the default physical network connection changed, e.g. wifi->mobile.
        linkChanged,

        // Indicates that the OS is preparing to suspend, so the VPN should drop its connection. The extension is not guaranteed to receive this event prior to suspending.
        suspend,

        // Indicates that the OS has resumed and the user has logged back in, so the VPN should try to reconnect.
        resume,
    }

    // The enum is used by the platform to indicate the event that triggered onUIEvent.
    enum class UIEvent {
        // Requests that the VPN client show the add configuration dialog box to the user.
        showAddDialog,

        // Requests that the VPN client show the configuration settings dialog box to the user.
        showConfigureDialog,
    }

    // The enum is used by the VPN client to inform the platform of its current state. This helps provide meaningful messages to the user.
    enum class VpnConnectionState {
        // Specifies that VPN connection was successful.
        connected,

        // Specifies that VPN connection has failed.
        failure
    }
}